package com.wcpdoc.exam.api.controller;

import com.wcpdoc.base.cache.ProgressBarCache;
import com.wcpdoc.core.context.UserContext;
import com.wcpdoc.core.controller.BaseController;
import com.wcpdoc.core.entity.*;
import com.wcpdoc.core.exception.MyException;
import com.wcpdoc.core.util.DateUtil;
import com.wcpdoc.core.util.SpringUtil;
import com.wcpdoc.core.util.StringUtil;
import com.wcpdoc.core.util.ValidateUtil;
import com.wcpdoc.exam.core.cache.QuestionCache;
import com.wcpdoc.exam.core.entity.*;
import com.wcpdoc.exam.core.entity.ex.ExamMockInfo;
import com.wcpdoc.exam.core.service.MyCollectService;
import com.wcpdoc.exam.core.service.MyExamMockService;
import com.wcpdoc.exam.core.service.MyMockQuestionService;
import com.wcpdoc.exam.core.util.ExamStateUtils;
import com.wcpdoc.exam.core.util.QuestionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

/**
 * 我的模拟考试控制层
 *
 */
@Controller
@RequestMapping("/api/myExamMock")
public class ApiMyExamMockController extends BaseController{
	private static final Logger log = LoggerFactory.getLogger(ApiMyExamMockController.class);

	@Resource
	private MyExamMockService myExamService;
	@Resource
	private MyMockQuestionService myQuestionService;
	@Resource
	private MyCollectService collectService;

	/**
	 * 我的模拟考试列表
	 *
	 * @return pageOut
	 */
	@RequestMapping("/listpage")
	@ResponseBody
	public PageResult listpage() {
		try {
			PageIn pageIn = new PageIn(request);
			pageIn.addAttr("curUserId", getCurUser().getId());
			PageOut pageOut = myExamService.getListpage(pageIn);
			for (Map<String, Object> map : pageOut.getList()) {
				Integer state =(Integer)map.get("state");
				Integer markState =(Integer)map.get("markState");
				BigDecimal totalScore =(BigDecimal)map.get("totalScore");
				BigDecimal passScore =(BigDecimal)map.get("passScore");
				map.put("myExamState", ExamStateUtils.getMyExamState(state, markState,totalScore,passScore));
			}

			return PageResultEx.ok().data(pageOut);
		} catch (Exception e) {
			log.error("我的模拟考试列表错误：", e);
			return PageResult.err();
		}
	}

	/**
	 * 模拟考试是否有未交卷的
	 *
	 */
	@RequestMapping("/examUnStart")
	@ResponseBody
	public PageResult examUnStart() {
		return PageResultEx.ok().data(myExamService.examUnStart(getCurUser().getId()));
	}

	/**
	 * 获取我的模拟考试
	 * 本接口可以不调用，后期前端可以优化掉
	 *
	 * @param examId
	 * @return PageResult
	 */
	@RequestMapping("/get")
	@ResponseBody
	public PageResult get(Integer examId) {
		try {
			MyExamMock myExam = myExamService.getMyExamMock(examId, getCurUser().getId());
			if (myExam == null) {
				throw new MyException("无查阅权限");
			}
			MyExamMock exam = myExamService.getEntity(examId);
			return PageResultEx.ok()
					.addAttr("examPassTime", exam.getPassTime()) // 考试时长
					.addAttr("examMarkState", exam.getMarkState()) // 页面控制是否显示错题
					.addAttr("examScoreState", exam.getScoreState())// 返回创建时间
					.addAttr("examName", exam.getPaperName())// 考试名称
					.addAttr("answerStartTime", ValidateUtil.isValid(myExam.getAnswerStartTime()) ? DateUtil.formatDateTime(myExam.getAnswerStartTime()) : null)
					.addAttr("answerEndTime", ValidateUtil.isValid(myExam.getAnswerEndTime()) ? DateUtil.formatDateTime(myExam.getAnswerEndTime()) : null)
//				.addAttr("markStartTime", ValidateUtil.isValid(myExam.getMarkStartTime()) ? DateUtil.formatDateTime(myExam.getMarkStartTime()) : null)
//				.addAttr("markEndTime", ValidateUtil.isValid(myExam.getMarkEndTime()) ? DateUtil.formatDateTime(myExam.getMarkEndTime()) : null)
					.addAttr("objectiveScore", myExam.getObjectiveScore())
					.addAttr("totalScore", scoreShow(exam) ? myExam.getTotalScore() : null)
					.addAttr("answerState", scoreShow(exam) ? myExam.getAnswerState() : null)
					.addAttr("state", myExam.getState())
					.addAttr("markState", myExam.getMarkState())
//				.addAttr("no", exam.getRankState() == 1 ? myExam.getNo() : null)
//				.addAttr("userNum", exam.getRankState() == 1 ? myExamService.getList(examId).size() : null)
					;
		} catch (MyException e) {
			log.error("获取我的考试错误：{}", e.getMessage());
			return PageResult.err().msg(e.getMessage());
		} catch (Exception e) {
			log.error("获取我的考试错误：", e);
			return PageResult.err();
		}
	}

	/**
	 * 我的模拟试卷
	 *
	 * @param examId
	 * @return PageResult
	 */
	@RequestMapping("/paper")
	@ResponseBody
	public PageResult paper(Integer examId) {
		try {
			// 校验数据有效性
			if (!ValidateUtil.isValid(examId)) {
				throw new MyException("参数错误：examId");
			}

			MyExamMock _myExam = myExamService.getMyExamMock(examId, getCurUser().getId());
			if (_myExam == null) {
				throw new MyException("无查阅权限");
			}
			MyExamMock exam = myExamService.getEntity(examId);
//			if (exam.getStartTime().getTime() > System.currentTimeMillis()) {
//				throw new MyException("考试未开始");
//			}

			// 组装试卷
			List<Map<String, Object>> paper = new ArrayList<>();
			List<MyMockQuestion> myQuestionList = myQuestionService.getList(examId, getCurUser().getId());

			boolean showAnswer = answerShow(exam);// 显示标准答案
			for (MyMockQuestion _myQuestion : myQuestionList) {
				Map<String, Object> myQuestion = new HashMap<>();
				if (_myQuestion.getType() == 1) {
					myQuestion.put("type", _myQuestion.getType());
					myQuestion.put("chapterName", _myQuestion.getChapterName());
					myQuestion.put("chapterTxt", _myQuestion.getChapterTxt());
				} else {
					myQuestion.put("type", _myQuestion.getType());
					myQuestion.put("questionId", _myQuestion.getQuestionId());

					Question question = QuestionCache.getQuestion(_myQuestion.getQuestionId());// 已关联考试的试题不会改变，缓存起来加速查询。
					myQuestion.put("questionType", question.getType());
					myQuestion.put("markType", question.getMarkType());
					myQuestion.put("title", question.getTitle());
//					myQuestion.put("markOptions", _myQuestion.getMarkOptions());
					myQuestion.put("score", _myQuestion.getScore());
					myQuestion.put("analysis", question.getAnalysis());
					myQuestion.put("userScore", scoreShow(exam) ?_myQuestion.getUserScore() : null);
					{// 选项
						List<String> options = new ArrayList<>();
						if (QuestionUtil.hasSingleChoice(question) || QuestionUtil.hasMultipleChoice(question)) {// 如果是单选或多选，添加选项字段
							List<QuestionOption> questionOptionList = QuestionCache.getOption(_myQuestion.getQuestionId());
							if (ValidateUtil.isValid(_myQuestion.getOptionsNo())) {// 选项乱序
								for (int optionsNo : _myQuestion.getOptionsNo()) {// 4,1,2,3
									QuestionOption questionOption = questionOptionList.get(optionsNo - 1);// 先放第4个
									options.add(questionOption.getOptions());
								}
							} else {
								for (QuestionOption questionOption : questionOptionList) {// 常规顺序
									options.add(questionOption.getOptions());
								}
							}

						}
						myQuestion.put("options", options);
					}

					{// 用户答案
						List<String> userAnswerList = new ArrayList<>();
						if (ValidateUtil.isValid(_myQuestion.getUserAnswer())) {
							if (QuestionUtil.hasTrueFalse(question) || QuestionUtil.hasQA(question)) {// 判断、问答
								userAnswerList.add(_myQuestion.getUserAnswer());
							} else if (QuestionUtil.hasSingleChoice(question)) {// 单选
								if (ValidateUtil.isValid(_myQuestion.getOptionsNo())) {// 选项乱序：4,1,3,2 ，填写答案：B，回显：D
									userAnswerList.add(_myQuestion.getOptionsNoCache().get(_myQuestion.getUserAnswer()));
								} else {
									userAnswerList.add(_myQuestion.getUserAnswer());
								}
							}  else if (QuestionUtil.hasMultipleChoice(question)) {//多选
								String[] userAnswers = _myQuestion.getUserAnswer().split(",");
								if (ValidateUtil.isValid(_myQuestion.getOptionsNo())) {
									for (String userAnswer : userAnswers) {
										userAnswerList.add(_myQuestion.getOptionsNoCache().get(userAnswer));
									}
								} else {
									Collections.addAll(userAnswerList, userAnswers);
								}
							} else if (QuestionUtil.hasFillBlank(question)) {// 填空
								Collections.addAll(userAnswerList, _myQuestion.getUserAnswer().split("\n", -1));
							}
						}
						myQuestion.put("userAnswers", userAnswerList);
					}

					{// 标准答案
						List<String> answerList = new ArrayList<>();
						if (showAnswer) {
							List<QuestionAnswer> questionAnswerList = QuestionCache.getAnswer(_myQuestion.getQuestionId());
							for(QuestionAnswer answer : questionAnswerList){
								if (QuestionUtil.hasTrueFalse(question)
										|| (QuestionUtil.hasQA(question) && QuestionUtil.hasSubjective(question))) {
									answerList.add(answer.getAnswer());
								} else if (QuestionUtil.hasSingleChoice(question)) {
									if (ValidateUtil.isValid(_myQuestion.getOptionsNo())) {
										answerList.add(_myQuestion.getOptionsNoCache().get(answer.getAnswer()));
									} else {
										answerList.add(answer.getAnswer());
									}
								} else if (QuestionUtil.hasMultipleChoice(question)) {
									String[] answers = answer.getAnswer().split(",");
									if (ValidateUtil.isValid(_myQuestion.getOptionsNo())) {
										for (String _answer : answers) {
											answerList.add(_myQuestion.getOptionsNoCache().get(_answer));
										}
									} else {
										Collections.addAll(answerList, answers);
									}
								} else if (QuestionUtil.hasFillBlank(question) || (QuestionUtil.hasQA(question)
										&& QuestionUtil.hasObjective(question))) {
									answerList.add(answer.getAnswer());
								}
							}
						}
						myQuestion.put("answers", answerList);
					}
				}
				paper.add(myQuestion);
			}

			return PageResultEx.ok().data(paper);
		} catch (MyException e) {
			log.error("我的试卷错误：{}", e.getMessage());
			return PageResult.err().msg(e.getMessage());
		} catch (Exception e) {
			log.error("我的试卷错误：", e);
			return PageResult.err();
		}
	}


	/**
	 * 开始考试
	 * @param examId
	 * @return
	 */
	@RequestMapping("/startExam")
	@ResponseBody
	public  PageResult startExam(Integer examId){
		try {
//			if (!AutoMarkCache.tryReadLock(examId, 2000)) {
//				throw new MyException("尝试加读锁失败");
//			}
			myExamService.startExam(examId, getCurUser().getId());
			return PageResult.ok();
		} catch (MyException e) {
			log.error("开始考试错误：{}", e.getMessage());
			return PageResult.err().msg(e.getMessage());
		} catch (Exception e) {
			log.error("开始考试错误：", e);
			return PageResult.err();
		}
//		finally {
//			AutoMarkCache.releaseReadLock(examId);
//		}
	}

	/**
	 * 答题
	 *
	 * @param examId
	 * @param questionId
	 * @param answers
	 * @return PageResult
	 */
	@RequestMapping("/answer")
	@ResponseBody
	public PageResult answer(Integer examId, Integer questionId, String[] answers) {
		try {
//			if (!AutoMarkCache.tryReadLock(examId, 2000)) {
//				throw new MyException("尝试加读锁失败");
//			}
			myExamService.answerUpdate(examId, getCurUser().getId(), questionId, answers);
			//自动保存错题
			LoginUser loginUser = getCurUser();
			new Thread(new Runnable() {
				public void run() {
					UserContext.set(loginUser);
					try {
						List<QuestionAnswer> questionAnswerList = QuestionCache.getAnswer(questionId);
						Question question = QuestionCache.getQuestion(questionId);
						boolean isFail = false;//错题标识
						if (ValidateUtil.isValid(questionAnswerList)&&ValidateUtil.isValid(answers)){
							if(questionAnswerList.size() == answers.length){
								for(QuestionAnswer answer : questionAnswerList){
									for (String _answer : answers) {
										if(!_answer.equals(answer.getAnswer())){
											isFail = true;
										}
									}
								}
							}
						}
						if(isFail){
							MyCollect myCollect = new MyCollect();
							myCollect.setUserAnswer(StringUtil.join(answers));
							myCollect.setType(1);
							myCollect.setUserId(loginUser.getId());
							myCollect.setQuestionTypeId(question.getQuestionTypeId());
							myCollect.setQuestionId(questionId);
							collectService.addCollect(myCollect);
						}else{
							collectService.delByQuestionId(questionId,loginUser.getId(),1);
						}
					} catch (MyException e) {
						UserContext.remove();
					} catch (Exception e) {
						UserContext.remove();
					}
				}
			}).start();
//			try {
//				List<QuestionAnswer> questionAnswerList = QuestionCache.getAnswer(questionId);
//				Question question = QuestionCache.getQuestion(questionId);
//				boolean isFail = false;//错题标识
//				if (ValidateUtil.isValid(questionAnswerList)&&ValidateUtil.isValid(answers)){
//					if(questionAnswerList.size() == answers.length){
//						for(QuestionAnswer answer : questionAnswerList){
//							for (String _answer : answers) {
//								if(!_answer.equals(answer.getAnswer())){
//									isFail = true;
//								}
//							}
//						}
//					}
//				}
//				if(isFail){
//					MyCollect myCollect = new MyCollect();
//					myCollect.setUserAnswer(StringUtil.join(answers));
//					myCollect.setType(1);
//					myCollect.setUserId(loginUser.getId());
//					myCollect.setQuestionTypeId(question.getQuestionTypeId());
//					myCollect.setQuestionId(questionId);
//					collectService.addCollect(myCollect);
//				}
//			} catch (MyException e) {
////						UserContext.remove();
//			} catch (Exception e) {
////						UserContext.remove();
//			}
			return PageResult.ok();
		} catch (MyException e) {
			log.error("答题错误：{}", e.getMessage());
			return PageResult.err().msg(e.getMessage());
		} catch (Exception e) {
			log.error("答题错误：", e);
			return PageResult.err();
		}
//		finally {
//			AutoMarkCache.releaseReadLock(examId);
//		}
	}

	/**
	 * 用户交卷
	 *
	 * @param examId
	 * @return PageResult
	 */
	@RequestMapping("/finish")
	@ResponseBody
	public PageResult finish(Integer examId) {
		try {
//			if (!AutoMarkCache.tryReadLock(examId, 2000)) {
//				throw new MyException("尝试加读锁失败");
//			}
			myExamService.finish(examId, getCurUser().getId());
			return PageResult.ok();
		} catch (MyException e) {
			log.error("用户交卷错误：{}", e.getMessage());
			return PageResult.err().msg(e.getMessage());
		} catch (Exception e) {
			log.error("用户交卷错误：", e);
			return PageResult.err();
		}
//		finally {
//			AutoMarkCache.releaseReadLock(examId);
//		}
	}

	/**
	 * 标准答案显示
	 *
	 * @param exam
	 * @return boolean
	 */
	private boolean answerShow(MyExamMock exam) {
		if (exam.getScoreState() == 1) {// 如果是考试结束后公布答案
			return true;
//			if (exam.getMarkType() == 1 && exam.getEndTime().getTime() < System.currentTimeMillis()) { // 如果是客观题试卷，考试结束后显示标准答案； 10:00-12:00 < 12:05
//				return true;
//			}
//			if (exam.getMarkType() == 2 && exam.getMarkEndTime().getTime() < System.currentTimeMillis()) {// 如果是主观题试卷，阅卷结束后显示标准答案
//				return true;
//			}
		}

		if (exam.getScoreState() == 3) {// 如果是交卷后公布答案
			return true;
//			if (myExam.getState() == 3) {// 如果用户已交卷，显示标准答案
//				return true;
//			}
		}

		return false;
	}

	/**
	 * 分数显示
	 *
	 * @param exam
	 * @return boolean
	 */
	private boolean scoreShow(MyExamMock exam) {
		return (exam.getScoreState() == 1 && exam.getMarkState() == 3) // 如果是考试结束后显示成绩，需要等到考试结束
				|| (exam.getScoreState() == 3 );// 如果是交卷后显示成绩，需要等到该试卷阅卷完成。比如主观题没阅卷，得不到总分，得不到是否及格
	}

	/**
	 * 模拟考试发布
	 *
	 * @param examInfo
	 * @return PageResult
	 */
	@RequestMapping("/publish")
	@ResponseBody
	public PageResult publish(@RequestBody ExamMockInfo examInfo) {
		try {
			ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
			RequestContextHolder.setRequestAttributes(requestAttributes, true);// 子线程共享请求属性

			String processBarId = UUID.randomUUID().toString().replaceAll("-", "");
			ProgressBarCache.setProgressBar(processBarId, 0.0, 1.0, "发布开始", HttpStatus.OK.value());// 放在前面，可能的问题为下面的线程执行慢，没有进度数据，前端显示空
//			Double processLen = (examInfo.getExamUserIds().length + 5) * 1.0;// 校验前数据处理+1，校验数据+1，保存考试+1，保存试卷+1，在业务层完成+1（事务内完成100%可能页面没刷新到），考试用户数量+userNum
			Double processLen = 5.0;
			LoginUser loginUser = getCurUser();
			new Thread(new Runnable() {
				public void run() {
					UserContext.set(loginUser);// 子线程不走springboot拦截器，人工模拟拦截器，线程上绑定当前登录信息
					try {
						examInfo.setGenType(2);
						examInfo.setName("模拟考试"+DateUtil.formatDate(new Date()));
						examInfo.setUserId(getCurUser().getId());
						examInfo.setScoreState(3);
						SpringUtil.getBean(MyExamMockService.class).publish(examInfo, processBarId);
						ProgressBarCache.setProgressBar(processBarId, processLen, processLen, "发布成功", HttpStatus.OK.value());// 放在业务层最后一行，进度已经100%，数据还没有完全插入，这时查询数据库时为空
					} catch (MyException e) {
						ProgressBarCache.setProgressBar(processBarId, 99.0, 100.0, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value());
						log.error("发布考试错误：{}", e.getMessage());
						UserContext.remove();
					} catch (Exception e) {
						ProgressBarCache.setProgressBar(processBarId, 99.0, 100.0, e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value());
						log.error("发布考试错误：", e);
						UserContext.remove();
					}
				}
			}).start();

			return PageResultEx.ok().data(processBarId);
		} catch (MyException e) {
			log.error("发布考试错误：{}", e.getMessage());
			return PageResult.err().msg(e.getMessage());
		} catch (Exception e) {
			log.error("发布考试错误：", e);
			return PageResult.err();
		}
	}


	@RequestMapping("/publishEx")
	@ResponseBody
	public PageResult publishEx() {
		try {
			MyExamMock myExamMock = myExamService.examUnStart(getCurUser().getId());
			if(myExamMock==null){
				return PageResultEx.ok().data(myExamService.genPaper());
			}
			return PageResultEx.ok().data(myExamMock.getId());
		} catch (MyException e) {
			log.error("发布考试错误：{}", e.getMessage());
			return PageResult.err().msg(e.getMessage());
		} catch (Exception e) {
			log.error("发布考试错误：", e);
			return PageResult.err();
		}
	}

	/**
	 * 是否存在正在进行的模拟考试
	 * @return
	 */
	@RequestMapping("/hasJxMockExam")
	@ResponseBody
	public PageResult hasJxMockExam(){
		MyExamMock myExamMock = myExamService.examUnStart(getCurUser().getId());
		return PageResultEx.ok().data(myExamMock);
	}


	/**
	 * 收藏模拟考试
	 * @return pageOut
	 */
	@RequestMapping("/del")
	@ResponseBody
	public PageResult del(Integer id) {
		try {
			if(!ValidateUtil.isValid(id)){
				throw new MyException("id error");
			}
			myExamService.delExamMockById(id);
			return PageResult.ok();
		} catch (MyException e) {
			return PageResult.err().msg(e.getMessage());
		}  catch (Exception e) {
			return PageResult.err();
		}
	}
}